

#include <assert.h>
#include "RenderEngine.h"
#include "EngineCommand.h"
#include "EngineManager.h"
#include "../../CommonLib/src/ThreadHelper.h"

namespace FlameEngine
{
	RenderEngine::RenderEngine(IRenderWindow* pRenderWnd)
		:m_pRenderWnd(pRenderWnd),
		m_pCoreEngine(nullptr),
		m_inited(false),
		m_frameCount(0)
	{
		m_cmdCallbacks.reserve((int32_t)ect_lastCmd);
		for(int32_t i = 0; i < (int32_t)ect_lastCmd; i++)
		{
			m_cmdCallbacks.emplace_back([](IEngineCommand* pCmd){});
		}
	}

	RenderEngine::~RenderEngine()
	{

	}

	void RenderEngine::Init(CoreEngine* pCoreEngine)
	{
		assert(pCoreEngine != nullptr && "RenderEngine is null!");

		m_pCoreEngine = pCoreEngine;
		m_inited = true;
	}

	void RenderEngine::StartRun()
	{
		ThreadHelper::SetThreadName("FlameEngine_Render");

		// 1/60 second
		uint32_t renderThreadUpdateInterval = 16;
		std::chrono::milliseconds maxSleepTime(renderThreadUpdateInterval);
		
		while(true)
		{
			std::unique_lock<std::mutex> lock(m_renderThreadMutex);
			m_renderThreadController.wait_for(lock, maxSleepTime, [&]{return !m_engineCmd.empty(); });
			
			//process cmd....
			IEngineCommand* pCmd = nullptr;

			{
				std::lock_guard<std::mutex> lock(m_engineCmdMtx);
				if(!m_engineCmd.empty())
				{
					pCmd = m_engineCmd.front();
					m_engineCmd.pop();
				}
			}

			if(pCmd)
			{
				ProcessEngineCmd(pCmd);
			}
				
		}
	}

	void RenderEngine::RegisterCmdCallback(uint16_t cmdType, EngineCmdCallback& callback)
	{
		assert(cmdType < ect_lastCmd && "engien command out of range!");
		m_cmdCallbacks[cmdType] = callback;
	}

	void RenderEngine::UnregisterCmdCallback(uint16_t cmdType)
	{
		if(cmdType < ect_lastCmd)
			m_cmdCallbacks[cmdType] = [](IEngineCommand* pCmd){};
	}

	void RenderEngine::ProcessEngineCmd(IEngineCommand* pCmd)
	{
		if(pCmd->IsExecutable())
		{
			return pCmd->Execute();
		}
		else
		{
			m_cmdCallbacks[pCmd->GetCmdType()](pCmd);
		}
	}

	void  RenderEngine::CreateWnd(IEngineCommand* pCmd)
	{
		if(pCmd == nullptr || pCmd->GetCmdType() != ect_createWnd)
			return;

		GetRenderWnd()->CreateAndRun((CreateWndCmd*)pCmd);
	}
}
